package service

import (
	"a-app/dao"
	"a-app/domain/entity"
	"a-app/domain/pojo"
	"a-common/auth/loginauth"
	"a-common/config"
	"a-common/constant"
	"a-common/core/redis"
	"a-common/myerr"
	"a-common/utils"
	"errors"
	"sync"

	"strconv"
	"time"

	"github.com/gofiber/fiber/v2"
)

var (
	appLoginServiceTemp *AppLoginService
	cryptUtils          = utils.NewCryptUtils()
	captchaUtils        = utils.NewCaptchaUtils()
	emailUtils          = utils.NewEmailUtils()
	loginDao            = dao.NewLoginDao()
	appLoginServiceOnce sync.Once
	rdCli               = redis.NewRedisCli()

	maxRetryCount = 5
	lockTime      = 10
)

type AppLoginService struct{}

func NewAppLoginService() *AppLoginService {
	appLoginServiceOnce.Do(func() {
		appLoginServiceTemp = &AppLoginService{}
	})
	return appLoginServiceTemp
}

// Login 用户登陆
func (m *AppLoginService) Login(args *pojo.AppLoginDto, ip string) (fiber.Map, error) {
	// 检测账号是否被锁定
	rdKey := constant.PWD_ERR_CNT_KEY + args.UserName
	cnt, _ := rdCli.Get(rdKey)
	count := 1
	str, _ := cnt.(string)
	if numInt, err := strconv.Atoi(str); err == nil {
		if numInt > maxRetryCount {
			return nil, myerr.ERR_LOGIN_LOCK
		}
		count = count + numInt
	}
	if err := rdCli.SetEx(rdKey, count, time.Duration(lockTime*60)); err != nil {
		return nil, err
	}

	// 校验验证码
	captchaEnable := config.Config.Login.CaptchaEnable
	if captchaEnable {
		if args.Uuid == "" {
			return nil, myerr.ERR_LOGIN_VERIFY
		}
		ok := captchaUtils.CaptchaVerify(args.Uuid, args.Code)
		if !ok {
			return nil, myerr.ERR_LOGIN_VERIFY
		}
	}

	// 获取数据库数据，并返回给前端
	userPo, err := loginDao.GetUserByAccount(args.UserName)
	if err != nil {
		return nil, err
	}
	if userPo.UserId == 0 {
		return nil, myerr.ERR_NOT_ACCOUNT
	}
	// 校验数据库密码
	if !cryptUtils.DeCrypt(userPo.Password, args.Password) {
		return nil, myerr.ERR_PWD
	}

	// 删除锁
	if err := rdCli.Del(rdKey); err != nil {
		return nil, err
	}
	// 构建短token
	token, err := loginauth.CreateShortToken(userPo.UserId, userPo.UserName, constant.APP, ip)
	if err != nil {
		return nil, err
	}
	// 构建长token
	refToken, err := loginauth.CreateLongToken(userPo.UserId, userPo.UserName, constant.APP, ip)
	if err != nil {
		return nil, err
	}
	// 更新最新登陆ip及时间
	err = appUserDao.EditByIpAndTime(userPo.UserId, ip, time.Now())
	if err != nil {
		return nil, err
	}
	// 返回
	return fiber.Map{
		"token":    token,
		"refToken": refToken,
	}, nil
}

// RefreshToken 刷新token
func (m *AppLoginService) RefreshToken(refToken string) (string, error) {
	return "", nil
}

// GetInfo 获取个人信息
func (m *AppLoginService) GetInfo(uid int64) (entity.AppUserPageVo, error) {
	// 获取用户信息
	return appUserDao.Get(uid)
}

// LoginOut 退出登陆
func (m *AppLoginService) LoginOut(account string) error {
	// 删除会话缓存
	return rdCli.Del(constant.LOGIN_SESSION + account)
}

// Register 注册用户
func (m *AppLoginService) Register(args *entity.UserDto) (int64, error) {
	// 先查询用户是否存在
	if has, err := appUserDao.CheckUserNameUnique(args.UserName, 0); has || err != nil {
		return 0, errors.ErrUnsupported
	}
	return loginDao.AddUser(args)
}

// CaptchaImage 获取图片验证码
func (m *AppLoginService) CaptchaImage() (id, b64s, answer string, err error) {
	return captchaUtils.Generate()
}

func (m *AppLoginService) CaptchaVerify(c *fiber.Ctx) error {
	id := c.Query("id")
	code := c.Query("code")
	ok := captchaUtils.CaptchaVerify(id, code)
	if !ok {
		return errors.New("验证失败")
	}
	return nil
}

// GetEmailCode 获取邮箱验证码
func (m *AppLoginService) GetEmailCode(email string) error {
	return emailUtils.SendCode(email, "验证码", "获取验证码")
}

// EmailCodeVerify 校验邮箱验证码
func (m *AppLoginService) EmailCodeVerify(email, code string) error {
	ok := emailUtils.Verify(email, code)
	if !ok {
		return errors.New("验证失败")
	}
	return nil
}
